/*
 * Copyright 2025 The Bazel Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.idea.blaze.android.run.runner;

import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.MoreCollectors;
import com.google.devtools.build.lib.rules.android.deployinfo.AndroidDeployInfoOuterClass.AndroidDeployInfo;
import com.google.devtools.build.lib.rules.android.deployinfo.AndroidDeployInfoOuterClass.Artifact;
import com.google.idea.blaze.android.manifest.ManifestParser;
import com.google.idea.blaze.android.manifest.ManifestParser.ParsedManifest;
import com.google.idea.blaze.base.scope.BlazeContext;
import com.google.idea.blaze.base.scope.output.StatusOutput;
import com.google.idea.blaze.common.artifact.OutputArtifact;
import com.intellij.openapi.util.text.StringUtil;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;

/** Utilities to create {@link DeployData} for specific targets. */
final class DeployDataExtractor {
  private DeployDataExtractor() {}

  public static DeployData extract(
      Collection<OutputArtifact> infoBuildArtifacts,
      Collection<OutputArtifact> apkBuildArtifacts,
      String deployInfoSuffix,
      BlazeContext context)
      throws IOException {
    // AndroidDeployInfo is a proto that is generated by Blaze and gives us the APKs to install
    // and the merged manifest file.
    AndroidDeployInfo deployInfoProto;
    try {
      context.output(new StatusOutput("Parsing deployment information."));
      deployInfoProto = fetchAndParseDeployInfo(infoBuildArtifacts, deployInfoSuffix);
    } catch (IOException e) {
      throw new IOException("Error getting deployinfo: " + e.getMessage(), e);
    }

    List<String> apkPaths =
        deployInfoProto.getApksToDeployList().stream()
            .map(Artifact::getExecRootPath)
            .map(DeployDataExtractor::getBazelOutRelativePath)
            .collect(toImmutableList());
    ImmutableList<OutputArtifact> apkArtifacts =
        apkBuildArtifacts.stream()
            .filter(a -> apkPaths.contains(getBazelOutRelativePath(a.getArtifactPath().toString())))
            .collect(toImmutableList());

    String mergedManifestPath = getBazelOutRelativePath(deployInfoProto.getMergedManifest().getExecRootPath());
    OutputArtifact manifestArtifact =
        infoBuildArtifacts.stream()
            .filter(a -> mergedManifestPath.equals(getBazelOutRelativePath(a.getArtifactPath().toString())))
            .collect(MoreCollectors.onlyElement());

    return DeployData.create(parseManifest(manifestArtifact), apkArtifacts);
  }

  private static String getBazelOutRelativePath(String str) {
    if (str.startsWith("blaze-out/") || str.startsWith("bazel-out/")) {
      return str.substring("blaze-out/".length());
    }
    return str;
  }

  private static AndroidDeployInfo fetchAndParseDeployInfo(
      Collection<OutputArtifact> bepOutput, String deployInfoSuffix) throws IOException {
    OutputArtifact deployInfoArtifact =
        bepOutput.stream()
            .filter(a -> a.getBazelOutRelativePath().endsWith(deployInfoSuffix))
            .collect(MoreCollectors.onlyElement());
    try (BufferedInputStream inputStream = deployInfoArtifact.getInputStream()) {
      return AndroidDeployInfo.parseFrom(inputStream);
    }
  }

  private static ParsedManifest parseManifest(OutputArtifact mergedManifestArtifact)
      throws IOException {
    try (InputStream is = mergedManifestArtifact.getInputStream()) {
      return ManifestParser.parseManifestFromInputStream(is);
    }
  }
}
